#include"Database.h"
#include<iostream>
#include<string>
using namespace std;

//数据库的构造与析构
Database::Database() {
	if (mysql_library_init(0, NULL, NULL)){  //初始化数据库
		cout << "不能够初始化数据库"<<endl;
		exit(1);
	}
	mysql = nullptr;
	state = false;
	field = nullptr;
	res = nullptr;
	row = nullptr;
	getQueryLotPtr = nullptr;
	mysql=mysql_init(mysql);  //数据库的指针初始化
	//cout << "数据库指针初始化成功" << endl;
	//没有写完
}
Database::~Database() {
    deleteGetQueryLotPtr();
	if (state == true)
		mysql_close(mysql);  //断开连接
	mysql_library_end();  //必要的操作，停止调用，释放内存
    //delete[]getQueryLotPtr;
	cout << "清理完成" << endl;
}
//连接数据库
bool Database::Connect(const char* ip, const int port, const char* name, const char* cypher, const char* database_name)	
{
	if (state == true) {
		cout << "数据库已经连接，无需重复连接" << endl;
		return true;
	}
	else {
		cout << "连接中..." << endl;
		if (!(mysql_real_connect(mysql, ip, name, cypher, database_name, port, NULL, 0))) //中间分别是主机，用户名，密码，数据库名，端口号
		{
			state = false;
			cout << "连接出错！"<<endl;
			mysql_error(mysql);
			return false;
		}
		else
		{
			state = true;
			cout<<"连接成功！"<<endl;
			return true;
		}
	}
}
//获取行数
int Database::getNumberOfRow(const char*table_name)
{
	string str = "SELECT * FROM ";
	str = str.append(table_name);  //变成命令“SELECT * FROM table_name”,选择表
	const char* querysentence= str.data();  //必须转换成这种形式，因为API只能支持char*，需要转换类型
	storeResult(querysentence);   //存储查询结果
	return mysql_affected_rows(mysql);
}
//获取列数
int Database::getNumberOfColumn(const char* table_name)
{
	string str = "DESC ";
	str = str.append(table_name);  //变成命令“DESC table_name”，获取列的属性
	const char* querysentence = str.data();  //必须转换成这种形式，因为API有问题
	storeResult(querysentence);   //获取并存储列属性
	return mysql_affected_rows(mysql); //见参考文档图片，也可以自己调试一下，用desc显示出来的列名是纵向排列的
}
//展示整个表
void Database::showTables(const char* table_name)
{
	//获取列数（字段数）
	int field = getNumberOfColumn(table_name);
	//生成mysql执行代码,执行并存储结果
	string str = "SELECT * FROM "; 
	str = str.append(table_name);  
	const char* querysentence = str.data();  //必须转换成这种形式，因为API有问题
	storeResult(querysentence);

	//打印
	cout << "数据总共有" << field << "列" << endl;
	//获取并打印列名称
	char **str_field=new char*[field];  //定义一个数组存储字段信息  
	for (int i = 0; i < field; i++)  //在已知字段数量的情况下获取字段名  
	{
		str_field[i] = mysql_fetch_field(res)->name;  //->name表明获取名称，这个取决于返回值结构体MYSQL_FIELD的数据成员
		printf("%10s\t", str_field[i]);//这里用了C语言的输出，因为我用cout不能很好的完成对齐
	}
	cout << endl;
	//打印获取的数据  
	while (row = FetchRow(res))   //fetch_row是用链表原理写成的，因此返回的是下一行！
	{
		for (int i = 0; i < field; i++)
		{
			printf("%10s\t", row[i]);//这里用了C语言的输出，因为我用cout不能很好的完成对齐
		}
		cout<<endl;
	}
	delete[]str_field;
}
void Database::showColumn(const char* table_name)
{
	//获取列数（字段数）
	int field = getNumberOfColumn(table_name);
	//生成mysql执行代码,执行并存储结果
	string str = "DESC ";
	str = str.append(table_name);
	const char* querysentence = str.data();  
	storeResult(querysentence);

	//打印
	//获取并打印列名称
	char** str_field = new char*[field];  //定义一个字符串数组存储字段信息  
	for (int i = 0; i < field; i++)  //在已知字段数量的情况下获取字段名  
	{
		str_field[i] = mysql_fetch_field(res)->name;  //->name表明获取名称，这个取决于返回值结构体MYSQL_FIELD的数据成员
		printf("%10s\t", str_field[i]);//这里用了C语言的输出，因为我用cout不能很好的完成对齐
	}
	cout << endl;
	//打印获取的数据  
	while (row = mysql_fetch_row(res))   //fetch_row是用链表原理写成的，因此返回的是下一行！
	{
		for (int i = 0; i < field; i++)
		{
			printf("%10s\t", row[i]);//这里用了C语言的输出，因为我用cout不能很好的完成对齐
		}
		cout << endl;
	}
	delete[]str_field;
}
//对数据库操作的时候选定一个表
bool Database::selectTable(const char* table_name)
{
	this->table_name = table_name;
	if (this->table_name == table_name)
		return true;
	else {
		cout << "选定表格（table）失败" << endl;
		return false;
	}
}
//数据的增删改
bool Database::addData(string _data)
{
		string str = "INSERT INTO ";
		str = str.append(table_name);
		str = str.append(" VALUES");
		str = str.append(_data);
		//变成命令“INSERT INTO table_name ”，显示整个表
		const char* querysentence = str.data();  //必须转换成这种形式，因为API接口不支持string
		return Sentence(querysentence);
}
//含有主键报警功能
bool Database::addDataPri(const char* PrimeColumnName, string PrimeColumnData, std::string data)
{
	string str = PrimeColumnName;
	str = str.append("=");
	str = str.append("'");
	str = str.append(PrimeColumnData);
	str = str.append("'");
	const char* condition = str.data();
	const char* table_name_temp= table_name.data();

	if (PrimeColumnData == getQuery(table_name_temp, condition, PrimeColumnName)) {
		cout << "主键已经存在数据，不能添加"<<endl;
		return false;
	}
	else {
		addData(data);
		return true;
		//cout << "没有重复项，添加成功" << endl;
	}
}
//创建一般表格
bool Database::createTable(const char* table_name, const char* columnNames)
{
	string str = "CREATE TABLE ";
	str = str.append(table_name);
	str = str.append(columnNames);
	const char* querysentence = str.data(); 
	return Sentence(querysentence);
}
//表的创建
bool Database::createTablePriAI(const char* table_name, int column)
{
	string str = "CREATE TABLE ";
	str = str.append(table_name);
	str = str.append(" (a INT AUTO_INCREMENT PRIMARY KEY");
	for (int i = 1; i <= column; i++) {
		str = str.append(",Column");
		char m[25];
		sprintf_s(m, "%d", i);  //运用了C语言中int向char的转换
		str = str.append(m);
		str = str.append(" TEXT");
	}
	str = str.append(")");
	const char* querysentence = str.data();  
	return Sentence(querysentence);   
}
//条件查询
bool Database::Query(const char* table_name, const char* condition,bool i/*=false*/)
{
	string str = "SELECT * FROM ";
	str = str.append(table_name);
	str = str.append(" WHERE ");
	str = str.append(condition);
	const char* querysentence = str.data(); 
	int field = getNumberOfColumn(table_name);//否则下面会改变res的值
	if (storeResult(querysentence)) {
		if (i)
			show(table_name,field);
		return true;
	}
	else {
		cout << "查询失败";
		return false;
	}
}
//返回查询结果
string Database::getQuery(const char* table_name, const char* condition, const char* getColumn) {
	string str = "SELECT ";
	str = str.append(getColumn);
	str = str.append(" FROM ");
	str = str.append(table_name);
	str = str.append(" WHERE ");
	str = str.append(condition);
	const char* querysentence = str.data();
	storeResult(querysentence);
	//还可以用mysql_data_seek(res, 0);
	//row = mysql_fetch_row(res);//注意，这里不能用mysql_fetch_row,这是链表写出来的，无法访问第一个！很容易造成row=NULL访问出错
	//分析：mysql_data_seek()函数任意一个（数组型）、mysql_fetch_row（）函数从前往后（链表型）、mysql_row_seek()函数从后往前（反向链表）
	row = FetchRow(res, "1");
	if (row==nullptr)
		return "0";
	else 
		return row[0];
}
//返回指针的
int Database::getQueryLot(const char* table_name, const char* condition, const char* getColumn,string*&Inptr) {
	string str = "SELECT ";
	str = str.append(getColumn);
	str = str.append(" FROM ");
	str = str.append(table_name);
	str = str.append(" WHERE ");
	str = str.append(condition);
	const char* querysentence = str.data();
	storeResult(querysentence);
	int numberOfrow = mysql_affected_rows(mysql);
	getQueryLotPtr = new string[numberOfrow];
	newGetQueryLotPtr(getQueryLotPtr);
	for(int i=0;i<numberOfrow;i++)
	{
		row = mysql_fetch_row(res);//fetch_row是用链表原理写成的，因此返回的是下一行！
		getQueryLotPtr[i] = row[0];
	}
	Inptr = getQueryLotPtr;
	return numberOfrow;
}
//任意指令输入
bool Database::Sentence(const char* sentence) {
	string querySentence = string(sentence);
	if (state == false) {
		cout << "数据库未连接"<<endl;
		return false;
	}
	else {
		unsigned int sentenceLength = querySentence.length();
		mysql_query(mysql, "set names gbk");//设置编码格式（SET NAMES GBK也行），否则cmd下中文乱码 
		//执行语句
		if ((mysql_real_query(mysql, sentence, sentenceLength))==0)  //这个地方有可能会出错，如果含有0或/g可能出错，见文档 https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-query.html
		{   
			//cout << "语句执行成功"<<endl;
			return true;
		}
		else {
			cout << "语句没有执行成功" << endl;
			cout<<mysql_error(mysql);  //输出错误信息
			exit(1);
			return false;
		}
	}
}  //string要不要重载一下
//带存储结果的指令输入
bool Database::storeResult(const char* anotherSentence)
{
	if (Sentence(anotherSentence))  //命令执行  
		if (!(res = mysql_store_result(mysql)))   //获得sql语句结束后返回的结果集res中
			//这个store_result消耗内存较大，如果数据量太大的化容易遇到超出内存的错误
			//可以用mysql_use_result()来提升性能，详细见 https://dev.mysql.com/doc/c-api/8.0/en/mysql-use-result.html
		{
			cout << "不能够存储结果,失败" << endl;
			cout<<mysql_error(mysql);
			return false;
		}
		else {
			//cout << "结果已经存储在res中，可以继续使用"<<endl;
			return true;
		}
}
//通用展示函数show
void Database::show(const char*table_name,int field) {
	char** str_field = new char* [field];  //定义一个数组存储字段信息  
	for (int i = 0; i < field; i++)  //在已知字段数量的情况下获取字段名  
	{
		str_field[i] = mysql_fetch_field(res)->name;  //->name表明获取名称，这个取决于返回值结构体MYSQL_FIELD的数据成员
		printf("%10s\t", str_field[i]);//这里用了C语言的输出，因为我用cout不能很好的完成对齐
	}
	cout << endl;
	//打印获取的数据  
	while (row = mysql_fetch_row(res))   //fetch_row是用链表原理写成的，因此返回的是下一行！
	{
		for (int i = 0; i < field; i++)
		{
			printf("%10s\t", row[i]);//这里用了C语言的输出，因为我用cout不能很好的完成对齐
		}
		cout << endl;
	}
	delete[]str_field;
}
//返回当前res值中的行数
int Database::Result_Rows()
{return mysql_affected_rows(mysql);}
//Fetch_Row的重写
MYSQL_ROW Database::FetchRow(MYSQL_RES* result, const char* row_number/*=ALL*/)
{
	//原理：mysql_data_seek()函数任意一个（数组型）、mysql_fetch_row（）函数从前往后（链表型）、mysql_row_seek()函数从后往前（反向链表）
	
	if (row_number == "ALL")
		return mysql_fetch_row(result);
	else {
		int rowNumber=atoi(row_number)-1;//atoi函数实现const Char*到int的安全转化！！！
		mysql_data_seek(res, rowNumber);
		return mysql_fetch_row(result);
	}
}
//储存产生new的指针
void Database::newGetQueryLotPtr(std::string* ptr)
{storePtr.insertRear(ptr);}
//
void Database::deleteGetQueryLotPtr()
{
	storePtr.reset(1);
    if(!(storePtr.isEmpty()))
    {
        for (int i = 0; i < (storePtr.getSize() - 1); i++)
        {
            string* temp;
            temp = storePtr.data();
            delete[]temp;
            storePtr.next();
        }
    }
}



//添加


void Database::tables()     //展示库中已有表
{
	char sql[1024]{ 0 };

	sprintf_s(sql, 1024, "show tables");



	///< 调用查询接口
	if (mysql_real_query(mysql, sql, (unsigned int)strlen(sql)))
	{
		cout << "查询失败"  << endl;
	}
	else
	{
		///< 装载结果集
		res = mysql_store_result(mysql);

		if (nullptr == res)
		{
			cout << "装载数据失败" << ": " << mysql_errno(mysql) << endl;
		}
		else
		{
			///< 取出结果集中内容
			while (row = mysql_fetch_row(res))
			{
				cout << row[0]  << endl;
			}
		}
	}

	///< 释放结果集
	mysql_free_result(res);
}



std::string Database::selectRandomRecord ()  //把tables改了一下来用    返回值为首列名
{
	mysql_query(mysql, "set names gbk");
	const char* t = table_name.data();   //连接表
   	
	std::string str = "select * from ";   
	str = str.append(table_name);
	str = str.append(" order by rand() limit 1");
	const char* querysentence = str.data();

	char sql[1024]{ 0 };
	sprintf_s(sql, 1024, querysentence);
	///< 调用查询接口
	if (mysql_real_query(mysql, sql, (unsigned int)strlen(sql)))
	{
		cout << "查询失败" << endl;
	}
	else
	{
		
		///< 装载结果集
		res = mysql_store_result(mysql);
		
		 std::string s;  //记录结果


		if (nullptr == res)
		{
			cout << "装载数据失败" << ": " << mysql_errno(mysql) << endl;
		}
		else
		{
			
			///< 取出结果集中内容
			row = mysql_fetch_row(res);
			s = row[0];    //记录下第一列的结果，这样写是为了方便后续查询
					
		
		}
		return s;
	}

	///< 释放结果集
	mysql_free_result(res);

}


